#include <jni.h>
#include <string>
#include "android/log.h"

// Windows 和 Linux 这两个宏是在 CMakeLists.txt 通过 ADD_DEFINITIONS 定义的
#ifdef Windows
#define __FILENAME__ (strrchr(__FILE__, '\\') + 1) // Windows下文件目录层级是'\\'
#elif Linux
#define __FILENAME__ (strrchr(__FILE__, '/') + 1) // Linux下文件目录层级是'/'
#else
#define __FILENAME__ (strrchr(__FILE__, '/') + 1) // 默认使用这种方式
#endif

#ifndef  LOG_TAG
#define  LOG_TAG    "Log4AndroidJNI"
#define  LOGI(format, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG,\
        "[%s][%s][%d]: " format, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__);
#define  LOGD(format, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,\
        "[%s][%s][%d]: " format, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__);
#define  LOGW(format, ...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG,\
        "[%s][%s][%d]: " format, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__);
#define  LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,\
        "[%s][%s][%d]: " format, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__);
#define  LOGV(format, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,\
        "[%s][%s][%d]: " format, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__);
#endif

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_mynativecplus_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
//    int* p = 0; //空指针
//    *p = 1; //写空指针指向的内存，产生SIGSEGV信号，造成Crash
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_mynativecplus_MainActivity_getproduct(JNIEnv *env, jclass clazz) {

    //首先获取class
    jclass cls = env->FindClass("com/example/mynativecplus/CProduct");
    if (cls == NULL) {
        return 0;
    }

    //定义类里面的属性
    jfieldID incode = env->GetFieldID(cls, "incode", "Ljava/lang/String;");
    jfieldID fname = env->GetFieldID(cls, "fname", "Ljava/lang/String;");
    jfieldID price = env->GetFieldID(cls, "price", "F");
    jfieldID qty = env->GetFieldID(cls, "qty", "I");

    //首先实例化类
    jobject jobj = env->AllocObject(cls);
    //然后对类中的各属性赋值
    //商品编码
    env->SetObjectField(jobj, incode, env->NewStringUTF("000001"));
    //商品名称
    env->SetObjectField(jobj, fname, env->NewStringUTF("康师傅方便面"));
    //价格
    env->SetFloatField(jobj, price, 6.5f);
    //数量
    env->SetIntField(jobj, qty, 10);

    //输出
    return jobj;

}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_mynativecplus_MainActivity_updateproduct(JNIEnv *env, jclass clazz, jobject prd) {

    //首先获取class
    jclass cls = env->FindClass("com/example/mynativecplus/CProduct");
    if (cls == NULL) {
        return 0;
    }

    //定义类里面的属性
    jfieldID incode = env->GetFieldID(cls, "incode", "Ljava/lang/String;");
    jfieldID fname = env->GetFieldID(cls, "fname", "Ljava/lang/String;");
    jfieldID price = env->GetFieldID(cls, "price", "F");
    jfieldID qty = env->GetFieldID(cls, "qty", "I");


    //然后对类中的各属性赋值
    //商品编码
    env->SetObjectField(prd, incode, env->NewStringUTF("000002"));
    //商品名称
    env->SetObjectField(prd, fname, env->NewStringUTF("康师傅冰红茶"));
    //价格
    env->SetFloatField(prd, price, 4.7f);
    //数量
    env->SetIntField(prd, qty, 15);

    //输出
    return prd;

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_mynativecplus_MainActivity_callJavaStaticMethod(JNIEnv *env, jclass clazz) {

    jstring str_arg = NULL;
    jmethodID mid_static_method;
    // 1、从classpath路径下搜索ClassMethod这个类，并返回该类的Class对象
    jclass cls = env->FindClass("com/example/mynativecplus/CMethod");
    if (cls == NULL) {
        return;
    }

    // 2、从cls类中查找callStaticMethod方法
    mid_static_method = env->GetStaticMethodID(cls, "callStaticMethod",
                                               "(Ljava/lang/String;I)V");
    if (mid_static_method == NULL) {
        printf("找不到callStaticMethod这个静态方法。");
        return;
    }

    // 3、调用cls类的callStaticMethod静态方法
    str_arg = (env)->NewStringUTF("我是静态方法");
    (env)->CallStaticVoidMethod(cls, mid_static_method, str_arg, 100);

    // 删除局部引用
    (env)->DeleteLocalRef(cls);
    (env)->DeleteLocalRef(str_arg);

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_mynativecplus_MainActivity_callJavaInstaceMethod(JNIEnv *env, jclass clazz) {

    jobject jobj = NULL;
    jmethodID mid_construct = NULL;
    jmethodID mid_instance = NULL;
    jstring str_arg = NULL;
    // 1、从classpath路径下搜索ClassMethod这个类，并返回该类的Class对象
    jclass mClazz = env->FindClass("com/example/mynativecplus/CMethod");
    if (mClazz == NULL) {
        printf("找不到'ClassMethod'这个类");
        return;
    }

    // 2、获取类的默认构造方法ID
    mid_construct = env->GetMethodID(mClazz, "<init>", "()V");
    if (mid_construct == NULL) {
        printf("找不到默认的构造方法");
        return;
    }

    // 3、查找实例方法的ID
    mid_instance = env->GetMethodID(mClazz, "callInstanceMethod", "(Ljava/lang/String;I)V");
    if (mid_instance == NULL) {
        return;
    }

    // 4、创建该类的实例
    jobj = env->NewObject(mClazz, mid_construct);
    if (jobj == NULL) {
        printf("在ClassMethod类中找不到callInstanceMethod方法");
        return;
    }

    // 5、调用对象的实例方法
    str_arg = env->NewStringUTF("我是实例方法");
    env->CallVoidMethod(jobj, mid_instance, str_arg, 200);

    // 删除局部引用
    env->DeleteLocalRef(mClazz);
    env->DeleteLocalRef(jobj);
    env->DeleteLocalRef(str_arg);

}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_mynativecplus_MainActivity_settextgood(JNIEnv *env, jobject instance,
                                                        jstring str_) {

    const char *str = env->GetStringUTFChars(str_, 0);

    //定义追加的字符
    char *addstr = "，我是追加的字符";
    //定义要输出的字符并设置长度
    char *outputstr = new char[strlen(str) + strlen(addstr)];
    //开始组装输出的字符
    //1.传入的字符拷贝进来
    strcpy(outputstr, str);
    //2.连接刚刚定义的追加字符
    strcat(outputstr, addstr);

    //释放资源
    env->ReleaseStringUTFChars(str_, str);

    return env->NewStringUTF(outputstr);

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_mynativecplus_MainActivity_showtextFromJNI(JNIEnv *env, jobject thiz) {

    //查找方法所在的类
    //获取jclass
    jclass jcls = env->GetObjectClass(thiz);
    if (jcls == NULL) {
        return;
    }

    //获取方法ID
    jmethodID jmethod = env->GetMethodID(jcls, "showText", "()V");
    if (jmethod == NULL) {
        return;
    }

    //调用方法
    env->CallVoidMethod(thiz, jmethod);

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_mynativecplus_MainActivity_showtextFromJNI2(JNIEnv *env, jobject thiz) {


    //查找方法所在的类
    //获取jclass
    jclass jcls = env->GetObjectClass(thiz);
    if (jcls == NULL) {
        return;
    }

    //获取方法ID
    jmethodID jmethod = env->GetMethodID(jcls, "showText2", "(Ljava/lang/String;)V");
    if (jmethod == NULL) {
        return;
    }

    char *outputstr = "我是第二个哈哈哈";

    //调用方法
    env->CallVoidMethod(thiz, jmethod, env->NewStringUTF(outputstr));

}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_mynativecplus_MainActivity_getlistproduct(JNIEnv *env, jclass clazz) {

    //        List<CProduct> products=new ArrayList<>();
    //        for (int i=0; i < 4; i++) {
    //            CProduct product=new CProduct();
    //            product.incode="000"+i;
    //            product.fname="商品"+i;
    //            product.price=3;
    //            product.qty=10;
    //
    //            products.add(product);
    //        }

    //获取ArrayList类引用
    jclass list_jcls = env->FindClass("java/util/ArrayList");
    if (list_jcls == NULL) {
        printf("ArrayList没找到相关类!");
        return 0;
    }

    //获取ArrayList构造函数id
    jmethodID list_init = env->GetMethodID(list_jcls, "<init>", "()V");
    //创建一个ArrayList对象
    jobject list_obj = env->NewObject(list_jcls, list_init);


    //获取ArrayList对象的add()的methodID
    jmethodID list_add = env->GetMethodID(list_jcls, "add", "(Ljava/lang/Object;)Z");


    //然后获取我们的CProduct类的class
    jclass jcls = env->FindClass("com/example/mynativecplus/CProduct");
    if (jcls == NULL) {
        return 0;
    }

    //定义类里面的属性
    jfieldID incode = env->GetFieldID(jcls, "incode", "Ljava/lang/String;");
    jfieldID fname = env->GetFieldID(jcls, "fname", "Ljava/lang/String;");
    jfieldID price = env->GetFieldID(jcls, "price", "F");
    jfieldID qty = env->GetFieldID(jcls, "qty", "I");

    for (int i = 0; i < 5; i++) {
        //首先实例化类
        jobject jobj = env->AllocObject(jcls);
        //然后对类中的各属性赋值
        //商品编码
        char *code = new char[5];
        sprintf(code, "0000%d", i);
        env->SetObjectField(jobj, incode, env->NewStringUTF(code));
        //商品名称
        char *tmpname = const_cast<char *>("名称");
        char *name = new char[strlen(tmpname) + strlen(code)];
        strcpy(name, tmpname);
        strcat(name, code);
        env->SetObjectField(jobj, fname, env->NewStringUTF(name));
        //价格
        env->SetFloatField(jobj, price, i);
        //数量
        env->SetIntField(jobj, qty, i + 10);

        env->CallBooleanMethod(list_obj, list_add, jobj);
    }

    return list_obj;

}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_mynativecplus_MainActivity_updatelistproduct(JNIEnv *env, jclass clazz,
                                                              jobject list) {

    //获取ArrayList类引用
    jclass list_jcls = env->FindClass("java/util/ArrayList");
    if (list_jcls == NULL) {
        printf("ArrayList没找到相关类!");
        return 0;
    }

    //获取ArrayList对象的get()的methodID
    jmethodID list_get = env->GetMethodID(list_jcls, "get", "(I)Ljava/lang/Object;");
    //获取ArrayList对象的size()的methodID
    jmethodID list_size = env->GetMethodID(list_jcls, "size", "()I");
    //然后获取我们的CProduct类的class
    jclass jcls = env->FindClass("com/example/mynativecplus/CProduct");
    if (jcls == NULL) {
        return 0;
    }
    jfieldID qty = env->GetFieldID(jcls, "qty", "I");

    int size = env->CallIntMethod(list, list_size);
    printf("%s", "size === " + size);
    //输出传入的a和b参数
    LOGI("size=%d", size);
    LOGE("size=%d", size);
    LOGW("size=%d", size);
    LOGD("size=%d", size);
    LOGV("size=%d", size);

    for (int i = 0; i < size; i++) {
        //通过List中的get方法获取到当前的CProduct类
        jobject item = env->CallObjectMethod(list, list_get, i);
        //获取到原来的数量
        int count = env->GetIntField(item, qty);
        //在原数量上加上20
        env->SetIntField(item, qty, count + 20);
    }

    return list;
}

//动态注册的方法1
//Java_com_example_mynativecplus_MainActivity_sayHello(JNIEnv *env, jobject thiz) {}
//静态注册/动态注册的方法名改变(不带包名/类名)，但参数不变
void sayHello(JNIEnv *env, jobject thiz) {
    printf("Hello World!\n");
    LOGE("This dynamic RegisterNatives fun\n");
}

//动态注册的方法2
jstring getDynamic(JNIEnv *env, jobject thiz) {
    return env->NewStringUTF("Hello Dynamic Native");
}

//动态注册的方法3
jfloat getDynamic2(JNIEnv *env, jobject thiz, jfloat jx, jfloat jy) {
    return jx + jy;
}

// 1、利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系.
//Java和JNI函数的绑定表
extern "C"
JNINativeMethod methods[] = {
        //前两个参数为Java映射，最后一个为native方法
        {"sayHello",        "()V",                  (void *) sayHello},
        //传参的函数声明
        {"dynamicFromJNI2", "(FF)F",                (void *) getDynamic2},
        {"dynamicFromJNI",  "()Ljava/lang/String;", (void *) getDynamic},
};

//2、实现 JNI_OnLoad 方法，在加载动态库后，执行动态注册.
extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }

    jclass javaClass = env->FindClass("com/example/mynativecplus/MainActivity");
    if (javaClass == NULL) {
        return JNI_ERR;
    }

    int method1 = sizeof(methods) / sizeof(methods[0]);
    int method2 = sizeof(methods) / sizeof(methods[1]);
    int method3 = sizeof(methods) / sizeof(methods[2]);
    if (env->RegisterNatives(javaClass, methods, method1) < 0) {
        return JNI_ERR;
    }
    if (env->RegisterNatives(javaClass, methods, method2) < 0) {
        return JNI_ERR;
    }
    if (env->RegisterNatives(javaClass, methods, method3) < 0) {
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}